/***************************************************************************//**
 *   @file   example.c
 *   @brief  Implementation of ssd1306 example for demo project.
 *   @author Robert Budai (robert.budai@analog.com)
********************************************************************************
 * Copyright 2025(c) Analog Devices, Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * 3. Neither the name of Analog Devices, Inc. nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY ANALOG DEVICES, INC. “AS IS” AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 * EVENT SHALL ANALOG DEVICES, INC. BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************/

#include "common_data.h"
#include "no_os_util.h"
#include <stdint.h>
#include <stdio.h>
#include "no_os_delay.h"
#include "no_os_i2c.h"
#include "display.h"
#include "ssd_1306.h"
#include "lvgl.h"
#include "lv_types.h"
#include "lv_display.h"
#include "lv_display_private.h"
#include "no_os_init.h"
#include <string.h>
#include "no_os_display.h"

#ifndef LV_ATTRIBUTE_MEM_ALIGN
#define LV_ATTRIBUTE_MEM_ALIGN
#endif

// SSD1306 setup
#define SSD1306_HOR_REZ 128
#define SSD1306_VER_REZ 64
#define SSD1306_BUFFER_SIZE ((SSD1306_HOR_REZ * SSD1306_VER_REZ) / 8 + 8)

// Buffer for UI usage

static uint8_t display_buffer[SSD1306_HOR_REZ * SSD1306_VER_REZ / 8 + 8];

const LV_ATTRIBUTE_MEM_ALIGN uint8_t star_wars_logo[] = {
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x3f, 0xff, 0xff, 0xff, 0xf8, 0x7f, 0xfc, 0x01, 0xff, 0xff, 0xc0, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xf8, 0x7f, 0xfc, 0x01, 0xff, 0xff, 0xe0, 0x00, 0x00,
	0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xff, 0xfe, 0x01, 0xff, 0xff, 0xf0, 0x00, 0x00,
	0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xff, 0xfe, 0x01, 0xff, 0xff, 0xf8, 0x00, 0x00,
	0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xf8, 0xff, 0xfe, 0x01, 0xff, 0xff, 0xfc, 0x00, 0x00,
	0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xfe, 0x01, 0xff, 0xff, 0xfc, 0x00, 0x00,
	0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xff, 0x01, 0xff, 0xff, 0xfc, 0x00, 0x00,
	0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xff, 0x01, 0xff, 0xff, 0xfe, 0x00, 0x00,
	0x00, 0x00, 0x03, 0xff, 0xff, 0xff, 0xff, 0xf9, 0xff, 0xff, 0x01, 0xff, 0x03, 0xfe, 0x00, 0x00,
	0x00, 0x00, 0x03, 0xff, 0x00, 0x1f, 0xe0, 0x01, 0xfe, 0xff, 0x81, 0xff, 0x03, 0xfe, 0x00, 0x00,
	0x00, 0x00, 0x03, 0xff, 0x80, 0x1f, 0xe0, 0x03, 0xfe, 0xff, 0x81, 0xff, 0x03, 0xfe, 0x00, 0x00,
	0x00, 0x00, 0x01, 0xff, 0xe0, 0x1f, 0xe0, 0x03, 0xfe, 0x7f, 0x81, 0xff, 0xff, 0xfc, 0x00, 0x00,
	0x00, 0x00, 0x01, 0xff, 0xe0, 0x1f, 0xe0, 0x03, 0xfc, 0x7f, 0xc1, 0xff, 0xff, 0xfc, 0x00, 0x00,
	0x00, 0x00, 0x00, 0xff, 0xf0, 0x1f, 0xe0, 0x07, 0xfc, 0x7f, 0xc1, 0xff, 0xff, 0xf8, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x7f, 0xf0, 0x1f, 0xe0, 0x07, 0xfc, 0x3f, 0xc1, 0xff, 0xff, 0xf0, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x3f, 0xf8, 0x1f, 0xe0, 0x07, 0xff, 0xff, 0xc1, 0xff, 0xff, 0xe0, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x3f, 0xf8, 0x1f, 0xe0, 0x0f, 0xff, 0xff, 0xe1, 0xff, 0xff, 0xe0, 0x00, 0x00,
	0x01, 0xff, 0xff, 0xff, 0xf8, 0x1f, 0xe0, 0x0f, 0xff, 0xff, 0xe1, 0xff, 0xff, 0xff, 0xf8, 0x00,
	0x01, 0xff, 0xff, 0xff, 0xf8, 0x1f, 0xe0, 0x0f, 0xff, 0xff, 0xe1, 0xff, 0xff, 0xff, 0xf8, 0x00,
	0x01, 0xff, 0xff, 0xff, 0xf8, 0x1f, 0xe0, 0x0f, 0xff, 0xff, 0xe1, 0xff, 0x7f, 0xff, 0xf8, 0x00,
	0x01, 0xff, 0xff, 0xff, 0xf8, 0x1f, 0xe0, 0x1f, 0xff, 0xff, 0xf1, 0xff, 0x3f, 0xff, 0xf8, 0x00,
	0x01, 0xff, 0xff, 0xff, 0xf0, 0x1f, 0xe0, 0x1f, 0xff, 0xff, 0xf1, 0xff, 0x3f, 0xff, 0xf8, 0x00,
	0x01, 0xff, 0xff, 0xff, 0xf0, 0x1f, 0xe0, 0x1f, 0xf0, 0x0f, 0xf1, 0xff, 0x1f, 0xff, 0xf8, 0x00,
	0x01, 0xff, 0xff, 0xff, 0xe0, 0x1f, 0xe0, 0x1f, 0xe0, 0x0f, 0xf1, 0xff, 0x0f, 0xff, 0xf8, 0x00,
	0x01, 0xff, 0xff, 0xff, 0x80, 0x1f, 0xe0, 0x3f, 0xe0, 0x0f, 0xf9, 0xff, 0x03, 0xff, 0xf8, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x01, 0xff, 0x8f, 0xfc, 0x3f, 0xe1, 0xff, 0xf0, 0x07, 0xff, 0xfc, 0x00, 0x01, 0xff, 0xf8, 0x00,
	0x01, 0xff, 0x8f, 0xfc, 0x7f, 0xc3, 0xff, 0xf0, 0x07, 0xff, 0xff, 0x00, 0x07, 0xff, 0xf8, 0x00,
	0x00, 0xff, 0x8f, 0xfe, 0x7f, 0xc3, 0xff, 0xf0, 0x07, 0xff, 0xff, 0xc0, 0x0f, 0xff, 0xf8, 0x00,
	0x00, 0xff, 0x8f, 0xfe, 0x7f, 0xc3, 0xff, 0xf0, 0x07, 0xff, 0xff, 0xe0, 0x1f, 0xff, 0xf8, 0x00,
	0x00, 0xff, 0xdf, 0xfe, 0x7f, 0xc7, 0xff, 0xf8, 0x07, 0xff, 0xff, 0xf0, 0x3f, 0xff, 0xf8, 0x00,
	0x00, 0xff, 0xdf, 0xfe, 0xff, 0x87, 0xff, 0xf8, 0x07, 0xff, 0xff, 0xf0, 0x3f, 0xff, 0xf8, 0x00,
	0x00, 0x7f, 0xdf, 0xff, 0xff, 0x87, 0xff, 0xf8, 0x07, 0xff, 0xff, 0xf0, 0x7f, 0xff, 0xf8, 0x00,
	0x00, 0x7f, 0xff, 0xff, 0xff, 0x87, 0xff, 0xf8, 0x07, 0xff, 0xff, 0xf8, 0x7f, 0xff, 0xf8, 0x00,
	0x00, 0x7f, 0xff, 0xff, 0xff, 0x8f, 0xff, 0xfc, 0x07, 0xfc, 0x0f, 0xf8, 0x7f, 0xff, 0xf8, 0x00,
	0x00, 0x3f, 0xff, 0xff, 0xff, 0x0f, 0xff, 0xfc, 0x07, 0xfc, 0x0f, 0xf8, 0x7f, 0xe0, 0x00, 0x00,
	0x00, 0x3f, 0xff, 0xff, 0xff, 0x0f, 0xfb, 0xfc, 0x07, 0xfc, 0x0f, 0xf8, 0x7f, 0xf0, 0x00, 0x00,
	0x00, 0x3f, 0xff, 0xff, 0xfe, 0x1f, 0xf3, 0xfe, 0x07, 0xff, 0xff, 0xf0, 0x3f, 0xfc, 0x00, 0x00,
	0x00, 0x1f, 0xff, 0xff, 0xfe, 0x1f, 0xf1, 0xfe, 0x07, 0xff, 0xff, 0xf0, 0x1f, 0xfe, 0x00, 0x00,
	0x00, 0x1f, 0xff, 0xff, 0xfe, 0x1f, 0xf1, 0xfe, 0x07, 0xff, 0xff, 0xe0, 0x1f, 0xfe, 0x00, 0x00,
	0x00, 0x1f, 0xff, 0xff, 0xfe, 0x1f, 0xe1, 0xfe, 0x07, 0xff, 0xff, 0xe0, 0x0f, 0xff, 0x00, 0x00,
	0x00, 0x1f, 0xff, 0xff, 0xfc, 0x3f, 0xff, 0xff, 0x07, 0xff, 0xff, 0x80, 0x07, 0xff, 0x00, 0x00,
	0x00, 0x0f, 0xff, 0xff, 0xfc, 0x3f, 0xff, 0xff, 0x07, 0xff, 0xff, 0x00, 0x07, 0xff, 0x00, 0x00,
	0x00, 0x0f, 0xff, 0xff, 0xfc, 0x3f, 0xff, 0xff, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
	0x00, 0x0f, 0xff, 0x3f, 0xfc, 0x3f, 0xff, 0xff, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
	0x00, 0x0f, 0xff, 0x3f, 0xf8, 0x7f, 0xff, 0xff, 0x87, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
	0x00, 0x07, 0xff, 0x3f, 0xf8, 0x7f, 0xff, 0xff, 0x87, 0xfd, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
	0x00, 0x07, 0xff, 0x3f, 0xf8, 0x7f, 0xff, 0xff, 0x87, 0xfc, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
	0x00, 0x07, 0xfe, 0x3f, 0xf8, 0x7f, 0xff, 0xff, 0xc7, 0xfc, 0x7f, 0xff, 0xff, 0xff, 0x00, 0x00,
	0x00, 0x07, 0xfe, 0x1f, 0xf0, 0xff, 0x80, 0x7f, 0xc7, 0xfc, 0x3f, 0xff, 0xff, 0xfe, 0x00, 0x00,
	0x00, 0x03, 0xfe, 0x1f, 0xf0, 0xff, 0x80, 0x3f, 0xc7, 0xfc, 0x1f, 0xff, 0xff, 0xfc, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

void my_flush_cb(lv_display_t * disp, const lv_area_t * area,
		 lv_color_t * color_p)
{
	int ret;

	uint8_t * lvgl_buff = &display_buffer[8];
	uint8_t buff_to_ssd1306[8 * 128] = {0};

	no_os_row_major_to_column_major_8bits(lvgl_buff, buff_to_ssd1306,
					      SSD1306_HOR_REZ, SSD1306_VER_REZ);

	ret = ssd_1306_print_buffer(oled_display, buff_to_ssd1306);
	lv_display_flush_ready(disp);
}

/**
 * @brief Initialize the display task
 *
 * @return int
 */
int display_task()
{
	int ret;
	lv_init();
	memset(display_buffer[8], 0x00, sizeof(display_buffer) - 8);
	lv_display_t * disp = lv_display_create(SSD1306_HOR_REZ, SSD1306_VER_REZ);
	lv_display_set_flush_cb(disp, my_flush_cb);
	lv_display_set_buffers(disp, display_buffer, NULL, sizeof(display_buffer),
			       LV_DISPLAY_RENDER_MODE_FULL);

	return 0;
}

/**
 * @brief Configure the UART
 *
 * @return int
 */
int configure_uart()
{
	int ret;
	struct no_os_uart_desc *demo_uart_desc;

	ret = no_os_uart_init(&demo_uart_desc, &demo_uart_ip);
	if (ret)
		return ret;

	no_os_uart_stdio(demo_uart_desc);
	return 0;
}

/**
 * @brief Configure the display
 *
 * @return int
 */
int configure_display()
{
	int ret;

	ret = no_os_i2c_init(&oled_display_i2c_desc, &oled_display_i2c_init_param);
	if (ret) {
		printf("Failed to initialize I2C.\n\r");
		return ret;
	}
	oled_display_extra.i2c_desc = oled_display_i2c_desc;
	ret = display_init(&oled_display, &oled_display_ini_param);
	if (ret) {
		printf("Failed to initialize display.\n\r");
		return ret;
	}

	return 0;
}

/**
 * @brief Test function to draw a label with scrolling text
 *
 */
void test_draw()
{
	lv_refr_now(NULL);
	const char * star_wars_intro =
		"Luke Skywalker has vanished. In his absence, the sinister FIRST \
		ORDER has risen from the ashes of the Empire.";

	lv_obj_t * label = lv_label_create(lv_scr_act());
	lv_label_set_long_mode(label, LV_LABEL_LONG_WRAP);
	lv_obj_set_width(label, SSD1306_HOR_REZ);
	lv_label_set_text(label, star_wars_intro);
	lv_obj_align(label, LV_ALIGN_BOTTOM_MID, 0, 0);

	// Animate the label to scroll upwards
	lv_anim_t anim;
	lv_anim_init(&anim);
	lv_anim_set_var(&anim, label);
	lv_anim_set_values(&anim, SSD1306_VER_REZ - lv_obj_get_height(label),
			   -lv_obj_get_height(label)); // Scroll from bottom to top
	lv_anim_set_time(&anim, 8000); // 5 seconds for the scroll
	lv_anim_set_exec_cb(&anim, (lv_anim_exec_xcb_t)lv_obj_set_y);
	lv_anim_start(&anim);

	lv_refr_now(NULL); // Force refresh to immediately render the animation
}

/**
 * @brief Display startup screen
 *
 */
void startup_screen()
{
	for (size_t i = 8; i < sizeof(display_buffer); i++) {
		display_buffer[i] = ~star_wars_logo[i - 8];
	}
	lv_display_t * disp = lv_display_get_default();
	lv_area_t area = {0, 0, SSD1306_HOR_REZ - 1, SSD1306_VER_REZ - 1};
	my_flush_cb(disp, &area, (lv_color_t *)&display_buffer[8]);
	no_os_mdelay(5000);
	lv_refr_now(NULL);
}

/**
 * @brief Main function
 *
 * @return int
 */
int example_main(void)
{
	int ret;

	no_os_init();

	ret = configure_uart();
	if (ret) {
		printf("Failed to initialize UART.\n\r");
		return ret;
	}
	printf("UART initialized.\n\r");
	ret = configure_display();
	if (ret) {
		printf("Failed to initialize display.\n\r");
		return ret;
	}
	printf("Display initialized.\n\r");
	ret = display_task();
	if (ret) {
		printf("Failed to initialize display task.\n\r");
		return ret;
	}
	printf("Display task started.\n\r");
	startup_screen();
	test_draw();

	while (1) {
		no_os_mdelay(20);
		lv_timer_handler();
	}
	return 0;
}
